package com.test.hand.client;

import com.test.hand.message.RpcRequestMessage;
import com.test.hand.message.RpcResponseMessage;
import com.test.hand.protocol.MessageCodecSharable;
import com.test.hand.protocol.ProtocolFrameDecoder;
import com.test.hand.server.handler.RpcRequestMessageHandler;
import com.test.hand.server.handler.RpcResponseMessageHandler;
import com.test.hand.server.service.HelloService;
import com.test.hand.server.service.HelloServiceImpl;
import com.test.hand.util.SequenceIdGenerator;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.DefaultPromise;
import lombok.Getter;

import java.lang.reflect.Proxy;

public class ClientManager {
    public static void main(String[] args) {
        HelloService service = getProxyService(HelloService.class);
        System.out.println(service.sayHello("zhangsan"));
        System.out.println(service.sayHello("Lisi"));
        System.out.println(service.sayHello("wangwu"));

    }

    public static <T> T getProxyService(Class<T> serviceClass) {
        ClassLoader classLoader = serviceClass.getClassLoader();
        Class<?>[] interfaces = new Class[]{serviceClass};
        Object o = Proxy.newProxyInstance(classLoader, interfaces, (proxy, method, args) -> {
                    int sequenceId = SequenceIdGenerator.nextId();
                    RpcRequestMessage message = new RpcRequestMessage(
                            sequenceId,
                            serviceClass.getName(),
                            method.getName(),
                            method.getReturnType(),
                            method.getParameterTypes(),
                            args
                    );
                    // 2.将消息发送
                    getChannel().writeAndFlush(message);

                    // 3.准备一个用于接收response的promise
                    DefaultPromise<Object> promise = new DefaultPromise<>(getChannel().eventLoop());
                    RpcResponseMessageHandler.PROMISES.put(sequenceId, promise);
                    // 4.等待promise的信息返回
                    promise.await();
                    // 如果promise的返回信息成功
                    if (promise.isSuccess()) {
                        return promise.getNow();
                    } else {
                        // 抛出异常
                        throw new RuntimeException(promise.cause());
                    }
                }
        );
        return (T) o;
    }


    static Channel channel = null;
    static Object LOCK = new Object();

    // 获取channel对象
    public static Channel getChannel() {
        if (channel != null) {
            return channel;
        }
        synchronized (LOCK) {
            if (channel != null) {
                return channel;
            }
            initChannel();
            return channel;
        }
    }

    public static void initChannel() {
        NioEventLoopGroup group = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        LoggingHandler LOGGING_HANDLER = new LoggingHandler(LogLevel.INFO);
        MessageCodecSharable MESSAGE_CODEC = new MessageCodecSharable();
        RpcResponseMessageHandler RPC_HANDLER = new RpcResponseMessageHandler();
        bootstrap.group(group);
        bootstrap.channel(NioSocketChannel.class);
        // 防粘包处理器，（根据message_codec编写的防粘包处理器）
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new ProtocolFrameDecoder());
                ch.pipeline().addLast(LOGGING_HANDLER);
                ch.pipeline().addLast(MESSAGE_CODEC);
                ch.pipeline().addLast(RPC_HANDLER);
            }
        });
        try {
            channel = bootstrap.connect("localhost", 8080).sync().channel();
            // 异步关闭channel
            channel.closeFuture().addListener(future -> {
                group.shutdownGracefully();
            });
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
    }
}
